//***************************************************************************
//   Copyright(c)2022, Xidian University D405.
//   All rights reserved
//
//   IP Name         :   Null
//   File name       :   np_dma_desc_cnt.v
//   Module name     :   np_dma_desc_cnt
//   Author          :   Wang Zekun
//   Date            :   2022/06/13
//   Version         :   v1.4
//   Verison History :   v1.0/v1.1/v1.2/v1.3/v1.4
//   Edited by       :   Wang Zekun
//   Modification history : v1.0 Initial revision
//                          v1.1 add clear_enable,every 32 desc is a pulse
//                          v1.2 add byte and address caculate
//                          v1.3 use timeout_valid to reset desc bytes and address, 
//                               and clear counter
//                          v1.4 cntr_32_integer width,load_desc_num could be changed at transmitting.
// ----------------------------------------------------------------------------
// Version 1.4      Date(2022/06/13)
// Abstract : minus module, only load enable and subtraction
//-----------------------------------------------------------------------------
// Programmer's model
//                    Null
//-----------------------------------------------------------------------------
//interface list :
//                single pulse Control
module np_dma_desc_cnt #(
  parameter AXI_LIB_WIDTH = 11,
  parameter AXI_ADDR_WIDTH = 40,
  parameter DESC_NUM = 13
  ) (
  input  wire                               clk_i,
  input  wire                               resetn_i,
  input  wire                               load_en_i,
  input  wire [DESC_NUM - 1 : 0]            load_desc_num_i,
  input  wire [AXI_ADDR_WIDTH-1 : 0]        base_address_i,
  input  wire                               desc_cnt_en_i,
  input  wire                               clr_en_i,
  input  wire                               rxfifo_timeout_valid_i,
  input  wire                               channel_ready_i,

  output wire                               cnt_stop_o,
  output wire [DESC_NUM - 1 : 0]            cnt_data_o,
  
  output wire                               cntr_32_integer_pulse_o,
  output wire [AXI_LIB_WIDTH - 1 : 0]       desc_bytes_ar_o,
  output wire [AXI_LIB_WIDTH - 1 : 0]       desc_bytes_aw_o,
  output wire [AXI_ADDR_WIDTH-1 : 0]        desc_addr_ar_o,
  output wire [AXI_ADDR_WIDTH-1 : 0]        desc_addr_aw_o,
  output wire [AXI_ADDR_WIDTH-1 : 0]        desc_addr_current_o
);
  
  reg  [DESC_NUM - 1 : 0]            down_count;

  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      down_count <= {DESC_NUM{1'b1}};
    end
    else if (clr_en_i)begin
      down_count <= {DESC_NUM{1'b1}};
    end
    else if (desc_cnt_en_i)begin
      down_count <= down_count - 1'b1;
    end
    else if (load_en_i)begin
      down_count <= load_desc_num_i;
    end
    else begin
      down_count <= down_count;
    end
  end

  /***********************************************************/
  /***********************************************************/
  /***********************************************************/

  //reg  [DESC_NUM - 5 : 0]            cntr_32_integer;
  reg  [DESC_NUM - 6 : 0]            cntr_32_integer;//[DESC_NUM-1:0]--->delete[4:0]--->[DESC_NUM-1:5]
  reg                                cntr_32_fraction;
  wire [AXI_LIB_WIDTH - 1 : 0]       desc_bytes_ar;
  reg  [AXI_LIB_WIDTH - 1 : 0]       desc_bytes_aw;
  reg  [AXI_ADDR_WIDTH - 1 : 0]      desc_addr_ar;
  reg  [AXI_ADDR_WIDTH - 1 : 0]      desc_addr_current;
  reg  [AXI_ADDR_WIDTH - 1 : 0]      desc_addr_aw;
  reg  [DESC_NUM - 1 : 0]            up_count;

  //the count result is equal to posedge desc_cnt_en_i,which is the number of descriptors already used
  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      up_count <= {DESC_NUM{1'b0}};
    end
    else if (cnt_stop_o | clr_en_i) begin
      up_count <= {DESC_NUM{1'b0}};
    end
    else if (desc_cnt_en_i) begin
      up_count <= up_count + 1'b1;
    end
    else begin
      up_count <= up_count;
    end
  end
  
  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      cntr_32_integer <= {DESC_NUM-5{1'b0}};
    end
    else if (clr_en_i)begin
      cntr_32_integer <= {DESC_NUM-5{1'b0}};
    end
    //else if (load_en_i)begin
    //  //cntr_32_integer <= load_desc_num_i[DESC_NUM-5:5];
    //  cntr_32_integer <= load_desc_num_i[DESC_NUM-1:5];
    //end
    else if (cntr_32_integer_pulse_o)begin
      cntr_32_integer <= cntr_32_integer + 1'b1;
    end
    else begin
      cntr_32_integer <= cntr_32_integer;
    end
  end

  assign cntr_32_integer_pulse_o = &up_count[4:0] & desc_cnt_en_i;

  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      cntr_32_fraction <= 1'b0;
    end
    else if (clr_en_i | rxfifo_timeout_valid_i) begin
      cntr_32_fraction <= 1'b0;
    end
    //else if (load_en_i) begin
    //  cntr_32_fraction <= |load_desc_num_i[4:0];
    //end
    else begin
      cntr_32_fraction <= |load_desc_num_i[4:0];//cntr_32_fraction;
    end
  end

  assign cnt_stop_o = (up_count == (load_desc_num_i-1'b1)) & desc_cnt_en_i;

  assign desc_bytes_ar  = (cntr_32_integer < load_desc_num_i[DESC_NUM-1:5]) ? {{AXI_LIB_WIDTH-10{1'b0}},10'b10_0000_0000} : //32*16B
                             cntr_32_fraction ? {{AXI_LIB_WIDTH-9{1'b0}},load_desc_num_i[4:0],4'b0000} :  //left 16B
                                                                      desc_bytes_aw;
  
  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      desc_bytes_aw <= {AXI_LIB_WIDTH{1'b0}};
    end
    else if (rxfifo_timeout_valid_i) begin
      desc_bytes_aw <= {{AXI_LIB_WIDTH-9{1'b0}},up_count[4:0],4'b0000};
    end
    else if (channel_ready_i) begin
      desc_bytes_aw <= desc_bytes_ar;
    end
    else begin
      desc_bytes_aw <= desc_bytes_aw;
    end
  end

  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      desc_addr_ar <= {AXI_ADDR_WIDTH{1'b0}};
    end
    else if (load_en_i)begin
      desc_addr_ar <= base_address_i;
    end
    else if (cntr_32_integer_pulse_o)begin
      desc_addr_ar <= desc_addr_ar + desc_bytes_ar;
    end
    else begin
      desc_addr_ar <= desc_addr_ar;
    end
  end
  
  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      desc_addr_aw <= {AXI_ADDR_WIDTH{1'b0}};
    end
    else if (load_en_i)begin
      desc_addr_aw <= base_address_i;
    end
    else if (cntr_32_integer_pulse_o | rxfifo_timeout_valid_i | cnt_stop_o)begin
      desc_addr_aw <= desc_addr_ar_o;
    end
    else begin
      desc_addr_aw <= desc_addr_aw;
    end
  end

  assign desc_addr_ar_o = desc_addr_ar;
  assign desc_addr_aw_o = desc_addr_aw;

  assign cnt_data_o    = down_count;
  assign desc_bytes_ar_o  = desc_bytes_ar;
  assign desc_bytes_aw_o  = desc_bytes_aw;//{load_desc_num_i[AXI_LIB_WIDTH-5:0],4'b0000};//

  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      desc_addr_current <= {AXI_ADDR_WIDTH{1'b0}};
    end
    else begin
      desc_addr_current <= base_address_i + {{AXI_ADDR_WIDTH-4-DESC_NUM{1'b0}},up_count,4'b0000};
    end
  end
  assign desc_addr_current_o = desc_addr_current;

endmodule